package org.xxpay.pay.channel.suile;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;
import org.xxpay.common.http.HttpRequestTemplate;
import org.xxpay.common.unify.AbstractPaymentConfig;
import org.xxpay.common.util.DateUtils;
import org.xxpay.common.util.XML;
import org.xxpay.common.util.sign.CertDescriptor;
import org.xxpay.common.util.sign.SignUtils;
import org.xxpay.common.util.sign.encrypt.Base64;
import org.xxpay.common.util.sign.encrypt.RSA;
import org.xxpay.common.util.str.StringUtils;
import org.xxpay.core.common.Exception.ServiceException;
import org.xxpay.core.common.constant.PayConstant;
import org.xxpay.core.common.constant.RetEnum;
import org.xxpay.core.entity.PayOrder;
import org.xxpay.pay.channel.BasePayment;

import java.io.UnsupportedEncodingException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Date;
import java.util.Map;

/**
 * @Package org.xxpay.pay.channel.suile
 * @Class: SuilePaymentService.java
 * @Description:
 * @Author leo
 * @Date 2019/6/1 0:18
 * @Version
 **/
@Component
public class SuilePaymentService extends BasePayment {

    private final static String logPrefix = "【随乐支付】";

    private static CertDescriptor certDescriptor;
    static {
        try {
            certDescriptor = new CertDescriptor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public String getChannelName() {
        return SuileConfig.CHANNEL_NAME;
    }

    @Override
    public JSONObject pay(PayOrder payOrder) {
        String channelId = payOrder.getChannelId();
        JSONObject retObj;
        switch (channelId) {
            case SuileConfig.CHANNEL_NAME_SUILE_QR:
                retObj = doOrderQrReq(payOrder);
                break;
            default:
                retObj = buildRetObj(PayConstant.RETURN_VALUE_FAIL, "不支持的渠道[channelId="+channelId+"]");
                break;
        }

        return retObj;
    }

    /**
     * 查询订单
     * @param payOrder
     * @return
     */
    @Override
    public JSONObject query(PayOrder payOrder) {

        SuileConfig config = new SuileConfig(getPayParam(payOrder));
        Map<String, Object> params = getorderQueryParameters(payOrder, config);
        _log.info("{}订单查询:{}", getChannelName(), JSON.toJSONString(params));
        // 请求参数转换成xml
        String originXml = XML.map2Xml(params, "message");
        String requestXml = getFinalXml(config, originXml);
        HttpRequestTemplate requestTemplate = new HttpRequestTemplate(null);
        String originRes = requestTemplate.postForObject(config.getRequestUrl(), requestXml, String.class);
        _log.info("{}请求响应：{}", logPrefix, originRes);
        // 响应对象
        JSONObject retObj = new JSONObject();
        try {
            String [] resBase64s = originRes.split("\\|");
            String responseXml = new String(Base64.decode(resBase64s[0]), SignUtils.CHARSET_UTF8);
            Map<String, Object> resultMap = XML.xml2Map(responseXml);
            JSONObject origin = JSONObject.parseObject(JSON.toJSONString(resultMap));
            _log.info("{}请求响应明文：{}", logPrefix, origin);

            if(origin.containsKey("deductList") && origin.getJSONArray("deductList").size() == 1) {
                JSONObject detailObj = origin.getJSONArray("deductList").getJSONObject(0);
                String payStatus = detailObj.getString("payStatus");
                if(StringUtils.equals(payStatus, SuileConfig.PAY_STATUS_OK)) {
                    retObj.put("status", 2); // 付款成功
                } else {
                    retObj.put("status", 1); // 支付中
                }
                retObj.put(PayConstant.RETURN_PARAM_RETCODE, PayConstant.RETURN_VALUE_SUCCESS);
                retObj.put("channelOrderNo", detailObj.getString("payOrderId"));
                retObj.put("channelObj", detailObj);
                return retObj;
            }
            retObj.put(PayConstant.RETURN_PARAM_RETCODE, PayConstant.RETURN_VALUE_FAIL);
            retObj.put(PayConstant.RESULT_PARAM_ERRDES, origin.getString("respDesc"));
            return retObj;

        } catch (Exception ex) {
            _log.error("{}请求下单异常", logPrefix, ex);
        }
        throw new ServiceException(RetEnum.RET_COMM_OPERATION_FAIL);
    }

    private JSONObject doOrderQrReq(PayOrder payOrder) {
        SuileConfig suileConfig = new SuileConfig(getPayParam(payOrder));

        Map<String, Object> reqParameters = getPublicParameters(suileConfig);
        reqParameters = getQrReqParameters(reqParameters, payOrder, suileConfig);
        _log.info("{}请求下单参数：{}", logPrefix, JSON.toJSONString(reqParameters));
        // 请求参数转换成xml
        String originXml = XML.map2Xml(reqParameters, "message");


        // 响应对象
        JSONObject finalRetObj = new JSONObject();
        try {
            String xmlBase64Sign = getSign(suileConfig, originXml);
            String xmlBase64 = Base64.encode(originXml.getBytes(SignUtils.CHARSET_UTF8));
            String requestXml = xmlBase64 + "|" + xmlBase64Sign;
            HttpRequestTemplate requestTemplate = new HttpRequestTemplate(null);
            String originRes = requestTemplate.postForObject(suileConfig.getRequestUrl(), requestXml, String.class);

            _log.debug("{}请求响应：{}", logPrefix, originRes);
            String [] resBase64s = originRes.split("\\|");
            String responseXml = new String(Base64.decode(resBase64s[0]), SignUtils.CHARSET_UTF8);
            Map<String, Object> resultMap = XML.xml2Map(responseXml);
            JSONObject origin = JSONObject.parseObject(JSON.toJSONString(resultMap));
            _log.info("{}请求响应明文：{}", logPrefix, origin);

            if(StringUtils.equals(origin.getString("respCode"), SuileConfig.RETURN_VALUE_SUCCESS)) {
                // 将订单更改为支付中
                int result = rpcCommonService.rpcPayOrderService.updateStatus4Ing(payOrder.getPayOrderId(), null);
                _log.info("[{}]更新订单状态为支付中:payOrderId={},channelOrderNo={},result={}", getChannelName(), payOrder.getPayOrderId(), null, result);
                String codeUrl = origin.getString("codeUrl");
                return buildPayResultOfCodeURL(finalRetObj, payOrder, codeUrl);
            }
            finalRetObj.put(PayConstant.RETURN_PARAM_RETCODE, PayConstant.RETURN_VALUE_FAIL);
            finalRetObj.put(PayConstant.RESULT_PARAM_ERRDES, origin.getString("respDesc"));
            return finalRetObj;
        } catch (UnsupportedEncodingException e) {
            _log.error("{}请求数据Base64字符串转byte异常", logPrefix, e);
        } catch (Exception ex) {
            _log.error("{}请求下单异常", logPrefix, ex);
        }

        finalRetObj.put(PayConstant.RETURN_PARAM_RETCODE, PayConstant.RETURN_VALUE_FAIL);
        finalRetObj.put(PayConstant.RESULT_PARAM_ERRDES, "渠道下单失败");
        return finalRetObj;
    }

    private String getFinalXml(AbstractPaymentConfig config, String originXml) {
        try {
            String xmlBase64Sign = getSign(config, originXml);
            String xmlBase64 = Base64.encode(originXml.getBytes(SignUtils.CHARSET_UTF8));
            String requestXml = xmlBase64 + "|" + xmlBase64Sign;
            return requestXml;
        } catch (UnsupportedEncodingException e) {
            _log.error("{}请求数据Base64字符串转byte异常", logPrefix, e);
        }
        return null;
    }

    private String getSign(AbstractPaymentConfig config, String requestXml) {
        _log.info("{}请求xml：{}", logPrefix, requestXml);
        certDescriptor.initPrivateSignCert(config.getPrivateStorePath(), config.getPrivateStorePathPwd(), "PKCS12");

        PrivateKey privateKey = certDescriptor.getSignCertPrivateKey(config.getPrivateStorePathPwd());
        byte[] md5Bytes = DigestUtils.md5(requestXml);
        try {
            byte[] signBytes = RSA.sign(md5Bytes, privateKey, "SHA1withRSA");
            return Base64.encode(signBytes);
        } catch (Exception e) {
            _log.info("{}签名异常", logPrefix, e);
        }

        return null;
    }

    public boolean signVerify(String body, String sign, AbstractPaymentConfig config) {
        certDescriptor.initPublicCert(config.getPublicStorePath());
        PublicKey publicKey = certDescriptor.getPublicCert().getPublicKey();
        return RSA.verify(body, sign, publicKey, SignUtils.CHARSET_UTF8);
    }

    private Map<String, Object> getQrReqParameters(Map<String, Object> parameters, PayOrder payOrder, SuileConfig suileConfig) {
        parameters.put("application", suileConfig.getPayMode());
        parameters.put("merchantOrderId", payOrder.getPayOrderId());
        parameters.put("merchantOrderAmt", payOrder.getAmount());
        parameters.put("merchantPayNotifyUrl", payConfig.getNotifyUrl(getChannelName()));
        parameters.put("merchantOrderDesc", payOrder.getSubject());
        parameters.put("userName", payOrder.getPayOrderId());
        return parameters;
    }

    private Map<String, Object> getorderQueryParameters(PayOrder payOrder, SuileConfig suileConfig) {
        Map<String, Object> parameters = Maps.newHashMap();
        parameters.put("merchantId", suileConfig.getMchId());
        parameters.put("version", SuileConfig.DEFAULT_VERSION);
        parameters.put("application", "QueryOrder");
        parameters.put("merchantOrderId", payOrder.getPayOrderId());
        return parameters;
    }

    private Map<String, Object> getPublicParameters(SuileConfig suileConfig) {
        Map<String, Object> parameters = Maps.newHashMap();
        parameters.put("merchantId", suileConfig.getMchId());
        parameters.put("version", SuileConfig.DEFAULT_VERSION);
        parameters.put("timestamp", DateUtils.YYYYMMDDHHMMSS.format(new Date()));
        return parameters;
    }

}
